<#
You are on the server team for a large metropolitan bank and you have been tasked by the team lead to produce a detailed report about services 
and dependent services. Specifically, your team lead wants you to report what services are currently running on each server. In addition, the 
team lead wants to see what dependent services each running service has and whether or not the dependent services are also running. All of your 
teams servers are running Windows Server 2008 R2, and your domain contains one server running Active Directory Domains Services (AD DS) on Windows 
Server 2008 R2. The remaining domain controllers are either Windows Server 2008 or Windows Server 2003.

For the purposes of this scenario, it is not necessary to write the results to a file, but the script should include the capability to run 
against multiple remote machines. The remote machine names should not be hard-coded, but they should come from a text file, Microsoft Excel 
spreadsheet, database, or AD DS. Because you are on the server team, you should not bother querying desktop machines. Your report should 
include only services that are running, but the report of dependent services should include all services regardless of whether they are 
running or not. Therefore, your output might look something like the output that is shown here.

BFE
-----
SharedAccess  Stopped
RemoteAccess  Stopped
PolicyAgent   Running

CryptSvc
--------
AppIDSvc      Stopped

* Extra points for querying AD DS for servers
* Extra points for including a filter to detect only computers that are running a server operating system
* Extra points for reusable code

Key Points:   Server TEAM, LARGE Metro BANK.
                Well formatted code, error handling, comment based help, ADSI searching AD DS.
              One DC 2008 R2 -> AD module available
              All Servers 2008 R2 -> PSRemoting available, presumably setup.
              Text File (Easy), Excel Spread (COMObject), Database (Table,Field,Auth), AD DS (Easy)
                Should be able to pipe computer names, provide a text file, or spec AD.
                WMI filter for text file or specced names, AD filter for AD DS query
              Reusable Code - Write Objects to pipeline and include a switch for formatted report.
                Parameters to control service filters, name/status/etc.
#>
param
(
  $serviceStatus = "Running",
  # 'Continue Pending','Paused','Pause Pending','Running','Start Pending','Stopped','Stop Pending'
  [System.String[]]$computerName = @('.')
)

function Start-BackgroundJobs
{
	<#
	.SYNOPSIS
	Executes a scriptblock as a background job for each item in a provided collection, passing the item as an argument to the scriptblock. Background job completion is tracked with a progress bar.
	.DESCRIPTION
	Often times we need to execute the same code over and over, with a single variation in each iteration.  That's what this function is built to do.  The two mandatory parameters for this function, scriptblock & collection, really allow for some flexible backgrounding of similar tasks.
	
	By default a progress bar is displayed while multiple jobs are executed simultaneously, you will not receive a command prompt until all jobs are no longer running.  If you want to resume working immediately use the -NoProgress switch, which will immediately write the job information to output and make you responsible for tracking job completion.
	
	The jobs are all executed on the local machine, execution of jobs on remote machines is not implemented.
	
	The output of this function is the job objects themselves, not the job results.  You must use Receive-Job in order to work with the results of the jobs.
	.PARAMETER Scriptblock
	The script that you want to execute for each item in the Collection.  Each Item in the collection is available in the scriptblock as $Args[0].
	.PARAMETER Collection
	 An array with arbitrary contents; for instance, a [String[]].  Each item in the array will be passed to the scriptblock via the $args variable.  But this doesn't mean you're limited to a single piece of information for each item!  If you need to pass in more complex data, get creative & pass a collection of custom objects, arrays, or hashtables, etc.
	.PARAMETER ProgressActivity
	Alters what the first line of the progress bar displays.
	.PARAMETER Credential
	Alternate credentials from the output of Get-Credential
	.PARAMETER NoProgress
	Suppresses the progress bar and immedately resumes execution after getting all the background jobs started.
	.EXAMPLE
	Start-BackgroundJobs -Collection "Win32_operatingsystem","Win32_processor","Win32_physicalmemory" -Scriptblock { Get-WMIObject $Args[0] } -ProgressActivity "Getting WMI info..." | Receive-Job
	
	Starts a background job for each string in in the collection, which match wmi classes.  Each item is available to the scriptblock as $Args[0], and so each job gets WMI information for one class.  The output of start-backgroundjobs is piped to Receive-Job to get the results
	#>
	[cmdletbinding()]
	param
	(
		[parameter(Mandatory=$true,Position=0)]
		[ValidateNotNullorEmpty()]
		[Scriptblock]
		$scriptblock,
		[parameter(Mandatory=$true,Position=1)]
		[ValidateNotNullorEmpty()]
		$collection,
		[parameter(Mandatory=$false,Position=2)]
		[ValidateNotNullorEmpty()]
		[String]
		$ProgressActivity = "Running Background Jobs...",
		[parameter(Mandatory=$false,Position=3)]
		[Management.Automation.PSCredential]
		$Credential,
		[switch]$NoProgress
	)
	# Create an array to store all background job information
	$arrJobs = @()
	# Start a background job for each reachable computer
	Write-Progress -Activity "Starting Background jobs..." `
	-Status "This may take some time."
	foreach ($item in $collection)
	{
		# If we have separate credentials use them to run the job
		if ($Credential -is [Management.Automation.PSCredential])
		{
			$arrJobs += Start-Job -ArgumentList $item -Credential $Credential -ScriptBlock $ScriptBlock
		}
		else # Run job as the current user
		{
			$arrJobs += Start-Job -ArgumentList $item -ScriptBlock $ScriptBlock
		}
	}

	if (-not($noProgress))
	{
		# Loop until All Jobs are no longer running, updating progress every 250ms
		do
		{
			[Object[]]$jobscomplete = $arrJobs | ? { $_.State -ne "Running" }
			# Jobs complete can be $null
			if ($jobscomplete -eq $null)
			{
				# If jobs is null we assign it to an array with 0 elements
				$jobscomplete = @()
			}
			Write-Progress -Activity $ProgressActivity `
			-Status $("{0}/{1} jobs complete." -f $jobsComplete.count,$arrJobs.Count)
			Start-Sleep -Milliseconds 250
		} until ($jobsComplete.Count -ge $arrJobs.Count)
		Write-Progress -Activity "DONE" -Status "DONE" -Completed
	}

	# Write job info to output
	foreach ($job in $arrJobs)
	{
		$job
	}
}

$sb = {
  $computerName = $args[0]
  $svcs = @(Get-Service -ComputerName $computerName)
  $svcs
}

$jobs = @(Start-BackGroundJobs -ScriptBlock $sb -Collection $computerName)
#  | ? { $_.status -eq $serviceStatus }
$jobResults = Receive-Job $jobs

foreach ($jr in $jobResults)
{
  $jr.location
  $jr
}
